/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dp1.titandevelop.titano.service;

import dp1.titandevelop.titano.bean.Estado;

import dp1.titandevelop.titano.persistent.Asignacion;
import dp1.titandevelop.titano.persistent.Demanda;
import dp1.titandevelop.titano.persistent.DetalleOrdenProduccion;
import dp1.titandevelop.titano.persistent.Kardex;
import dp1.titandevelop.titano.persistent.OrdenProduccion;
import dp1.titandevelop.titano.persistent.Producto;
import dp1.titandevelop.titano.persistent.ProductoXProceso;
import dp1.titandevelop.titano.persistent.Receta;
import dp1.titandevelop.titano.persistent.Requerimiento;
import dp1.titandevelop.titano.persistent.TipoMovimiento;
import dp1.titandevelop.titano.persistent.auto._Kardex;
import dp1.titandevelop.titano.view.LoginT;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.cayenne.DataObjectUtils;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.map.SQLResult;
import org.apache.cayenne.query.SQLTemplate;
import org.apache.cayenne.query.SelectQuery;

/**
 *
 * @author andres
 */
public class OrdenProduccionService {

    public ArrayList<OrdenProduccion> buscarPorRangoFechas(Date fi, Date ff) {
        ObjectContext context = DataContext.createDataContext();
        Expression qualifier = ExpressionFactory.greaterExp(OrdenProduccion.ESTADO_PROPERTY, 0);

        if ( fi != null ) 
            qualifier.andExp(ExpressionFactory.greaterOrEqualExp(OrdenProduccion.FECHA_PROPERTY, fi));
        
        if ( ff != null )
            qualifier.andExp(ExpressionFactory.lessOrEqualExp(OrdenProduccion.FECHA_PROPERTY, ff));

        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        return (ArrayList<OrdenProduccion>) context.performQuery(select);
    }
    
    

    public OrdenProduccion buscarPorId(int id) {
        ObjectContext context = DataContext.createDataContext();
        Expression qualifier = ExpressionFactory.matchExp(OrdenProduccion.IDORDENPRODUCCION_PROPERTY, id);
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        return (OrdenProduccion) DataObjectUtils.objectForQuery(context, select);
    }
    
    public OrdenProduccion buscarPorId(ObjectContext c,int id) {
        Expression qualifier = ExpressionFactory.matchExp(OrdenProduccion.IDORDENPRODUCCION_PROPERTY, id);
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        return (OrdenProduccion) DataObjectUtils.objectForQuery(c, select);
    }
    
    public List<OrdenProduccion> buscarTodos() {
        ObjectContext context = DataContext.createDataContext();
        Expression qualifier = ExpressionFactory.greaterExp(OrdenProduccion.ESTADO_PROPERTY, 0);
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        return context.performQuery(select);
    }
 
    public OrdenProduccion buscarActivo() {
        ObjectContext context = DataContext.createDataContext();
        Expression qualifier = ExpressionFactory.matchExp(OrdenProduccion.ESTADO_PROPERTY, 1);
        qualifier = qualifier.orExp(ExpressionFactory.matchExp(OrdenProduccion.ESTADO_PROPERTY, 11));
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        ArrayList<OrdenProduccion> op = new ArrayList<OrdenProduccion>();
        op.addAll(context.performQuery(select));

        if (op.isEmpty()) {
            return null;
        }
        // ya habia otro pero el mio es mas bonito
        return op.get(0);
    }
        
    public boolean esta(int id, List<Integer> idproductos ){
    
        for(int i=0;i<idproductos.size();i++){
            if(id==idproductos.get(i))
                return true;
        }
        return false;
    }
    public void generaOP(List<Asignacion> asig) {// Orden de Produccion por día
        
        ObjectContext context = DataContext.createDataContext();//CONTEXTO GENERAL
        Estado estado= new Estado();
        
        int codasig = asig.get(0).getCodigoasignacion(); //obtengo el ID de asignación que me servirá para recorrer todos los productos.
        List<Integer> idproductos = new ArrayList<Integer>();//lista de productos terminados de mi detalle de orden de produccion
        Producto prod;
        
        MaquinaService servM = new MaquinaService();
        
        for(int i=0;i<asig.size();i++){// Agrego los productos terminados que se producirán
            
//            dp1.titandevelop.titano.service.ProductoXProcesoService(asig.get(i).getToMaquina().getIdmaquina());
//            asig.get(i).
            prod=asig.get(i).getToProducto();
            if(prod.getToTipoProducto().getDescripcion().compareTo("Producto Terminado")==0){
                if(!esta(prod.getIdproducto(),idproductos))
                    idproductos.add(prod.getIdproducto());
            }
        }
        
        Date hoy = new Date();
        //Creo la orden de produccion cabecera
        OrdenProduccion p = context.newObject(OrdenProduccion.class);
        Demanda dem = DemandaService.buscarPorFecha(context, hoy);//Deberia haber un meotdo saca Demanda Activa!
        
        p.setFecha(hoy);        
        p.setToDemanda(dem);       
        p.setEstado(estado.getActivo());//Por ahora se crea pendiente pero activa
        
        context.commitChanges();

        int idOrdenProduccion = p.getIdordenproduccion();
        
        generaDetalleOP(idOrdenProduccion,codasig,idproductos);    
        
        String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaInsert(usuario, perfil, "Orden Produccion");
   }
   
    
  private void creaDetalleOP (Producto prod, int idOP, float cantidad, int idAsignacion)
    {
        RecetaService sr= new RecetaService();
        ProductoService sp=new ProductoService();
        AsignacionService sa=new AsignacionService();
        AlmacenService servAlm = new AlmacenService();
	ArrayList<Receta> lr = sr.BuscarPorProducto(prod);//me bota las entradas para un producto
        //Inserta Detalle					
	int codDetalle=insertarDetalleOP1(idOP ,prod.getIdproducto(), (int)cantidad);   
                                      
                    for (int i=0; i<lr.size();i++)
                    {                        
			float cant=lr.get(i).getCantidad()*cantidad;
			float merma= sa.ObtenerRoturaPorProcesoAsignaicion(idAsignacion,prod.getToProceso().getIdproceso());//cantidad adicional a producir del producto de salida
			float merma1= lr.get(i).getCantidad()*merma;
			float total=cant+merma1;
			float stock=sp.Stock(lr.get(i).getToProducto1().getIdproducto());
			//Inserta Movimiento de manterial de salida para orden de produccion de todos sus insumos excepto a productos terminados
			if (lr.get(i).getToProducto1().getToTipoProducto().getIdtipoproducto()!=3)servAlm.movimientoAlmacen(codDetalle, lr.get(i).getToProducto1().getIdproducto(),3,(int)total);
			if (total>stock)  //Aqui se puede meter condiciones para stock minimo.
			{	
				if (lr.get(i).getToProducto1().getToTipoProducto().getIdtipoproducto()!=1)//Se podria aprovechar en madnar requerimientos
				{
					creaDetalleOP(lr.get(i).getToProducto1(),idOP, total - stock, idAsignacion);
				}
			}
			
                    }
                    
    }
         
 public void generaOP() {// Orden de Produccion por día
        
        ObjectContext context = DataContext.createDataContext();//CONTEXTO GENERAL
        Estado estado= new Estado();
        AsignacionService sa=new AsignacionService();
        ProductoService sp=new ProductoService();
        int codasig = sa.ultimoId(context); //obtengo el ID de asignación que me servirá para recorrer todos los productos.
        List<Integer> idproductos = new ArrayList<Integer>();//lista de productos terminados de mi detalle de orden de produccion
        Producto prod;
        
        MaquinaService servM = new MaquinaService();
        ArrayList<Asignacion> asig=sa.SacaUltimo();
        for(int i=0;i<asig.size();i++){// Agrego los productos terminados que se producirán
            
//            dp1.titandevelop.titano.service.ProductoXProcesoService(asig.get(i).getToMaquina().getIdmaquina());
//            asig.get(i).
            prod=asig.get(i).getToProducto();
            if(prod.getToTipoProducto().getDescripcion().compareTo("Producto Terminado")==0){
                if(!esta(prod.getIdproducto(),idproductos))
                    idproductos.add(prod.getIdproducto());
            }
        }
        
        Date hoy = new Date();
        //Creo la orden de produccion cabecera
        OrdenProduccion p = context.newObject(OrdenProduccion.class);
        Demanda dem = DemandaService.buscarPorFecha(context, hoy);//Deberia haber un meotdo saca Demanda Activa!
        
        p.setFecha(hoy);        
        p.setToDemanda(dem);       
        p.setEstado(estado.getActivo());//Por ahora se crea pendiente pero activa
        
        context.commitChanges();

        int idOrdenProduccion = p.getIdordenproduccion();
        
      //Para cada elemento de mi lista de productos terminados agregare su lista de insumos y productos intermedios necesarios a conseguir
        for(int i=0;i<idproductos.size();i++)
        {
           Producto prod1= sp.buscarPorId(idproductos.get(i));
           float prodTotal = sa.ObtenerProduccionPorGalletaAsignaicion(codasig, prod1.getIdproducto())+
                              sa.ObtenerRoturaPorGalletaAsignaicion(codasig,prod1.getIdproducto());  
            
            int stockTotal = sp.Stock(prod1.getIdproducto());
            
            int requerimiento = 200;//Math.round(prodTotal-stockTotal);
           if(requerimiento>0) this.creaDetalleOP(prod1, idOrdenProduccion, requerimiento, codasig);
   }
        
         String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaInsert(usuario, perfil, "Orden Produccion");
 }
private int insertarDetalleOP1(int idOrdenProduccion,int idProducto ,int cantidad){
        
        Estado est = new Estado();
        ObjectContext context = DataContext.createDataContext();
        ProductoService servP = new ProductoService();
        
        OrdenProduccion op = this.buscarPorId(context,idOrdenProduccion);
        Producto p = servP.buscarPorId(context, idProducto);
        
        DetalleOrdenProduccion a = context.newObject(DetalleOrdenProduccion.class);
        a.setToOrdenProduccion(op);
        a.setToProducto(p);
        a.setCantidad(cantidad);
        a.setEstado(est.getActivo());
        
        context.commitChanges();
        
        int idDOP = a.getIddetalleordenproduccion();
         String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaInsert(usuario, perfil, "Orden Produccion");
       return idDOP;
    }  

private void insertarDetalleOP(int idOrdenProduccion,int idProducto,int requerimiento){
        
        Estado est = new Estado();
        ObjectContext context = DataContext.createDataContext();
        ProductoService servP = new ProductoService();
        
        OrdenProduccion op = this.buscarPorId(context,idOrdenProduccion);
        Producto p = servP.buscarPorId(context, idProducto);
        
        DetalleOrdenProduccion a = context.newObject(DetalleOrdenProduccion.class);
        a.setToOrdenProduccion(op);
        a.setToProducto(p);
        a.setCantidad(requerimiento);
        a.setEstado(est.getActivo());
        
        context.commitChanges();
        
        int idDOP = a.getIddetalleordenproduccion();
        //Luego de haberse realizado el detalle de la orden de produccion movemos los productos necesarios
        especificaMovimientos(idDOP,idProducto,requerimiento);
         String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaInsert(usuario, perfil, "Orden Produccion");
    }
    public void cambioEstadoDetalleOP_EnProceso(int idDOP){//a estado cumplido o en proceso
    
        Estado  estado = new Estado();
        ObjectContext context = DataContext.createDataContext();
        
        DetalleOrdenProduccion dop = this.buscarDetalleOrdProdPorIdDOP(context, idDOP);
        dop.setEstado(estado.getEnProceso());                
        dop.getToOrdenProduccion().setEstado(estado.getEnProceso());
        
        context.commitChanges();
         String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaUpdate(usuario, perfil, "Orden Produccion");
    }
    public void cambioEstadoDetalleOP_Cumplido(int idDOP){//a estado cumplido o en proceso
    
        Estado  estado = new Estado();
        ObjectContext context = DataContext.createDataContext();
        
        DetalleOrdenProduccion dop = this.buscarDetalleOrdProdPorIdDOP(context, idDOP);
        dop.setEstado(estado.getCumplido());
        if(verificaCumplidos(dop.getToOrdenProduccion(),idDOP)){
            dop.getToOrdenProduccion().setEstado(estado.getCumplido());
        }         
        context.commitChanges();
        String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaUpdate(usuario, perfil, "Orden Produccion");
    }
    
    public void generaDetalleOP(int idOrdenProduccion, int codasig, List idproductos) {
        
        AsignacionService servAsig = new AsignacionService();
        AlmacenService servAlm = new AlmacenService();

        for (int i = 0; i < idproductos.size(); i++) {
            
            int idProducto = (Integer)idproductos.get(i);           
            
            float prodTotal = (servAsig.ObtenerProduccionPorGalletaAsignaicion(codasig, idProducto)+
                              servAsig.ObtenerRoturaPorGalletaAsignaicion(codasig,idProducto))*30;  
            
            int stockTotal = servAlm.capacidad_StockTotalProducto(idProducto).get(1);
            
            int requerimiento = Math.round(prodTotal-stockTotal);
            
            if(requerimiento > 0){//Falta en stock, por lo tanto se produce                
                this.insertarDetalleOP(idOrdenProduccion, idProducto, requerimiento);        
            }           
            //Si el requerimiento es menor igual que "0" quiere decir que hay suficiente stock en almacen del producto
         
        }  
         String usuario=LoginT.userLogin.getUsuario();
        String perfil=LoginT.perfilLogin.getDescripcion();
        AuditoriaService as = new AuditoriaService();
        as.auditoriaInsert(usuario, perfil, "Orden Produccion");
    }

    private void especificaMovimientos(int idDOP, int idProducto, int requerimiento) {
        
        RecetaService servRec = new RecetaService();
        AlmacenService servAlm = new AlmacenService();
        
        List<Receta> insumos = servRec.BuscarPorIdProducto(idProducto);
        if(!insumos.isEmpty()){
            for(int i=0;i<insumos.size();i++){
                
                Producto p = insumos.get(i).getToProducto1();
                
                if(p.getToTipoProducto().getDescripcion().compareTo("Insumo")==0){//Empaques

                    int aMover = Math.round(insumos.get(i).getCantidad())* requerimiento;
                    servAlm.movimientoAlmacen(idDOP, p.getIdproducto(),3,aMover);                    
                }
                else{//Producto Intermedio

                    int aMover = Math.round(insumos.get(i).getCantidad())* requerimiento;
                    
                    servAlm.movimientoAlmacen(idDOP, p.getIdproducto(),3,aMover);
                    
                    int stockTotal = servAlm.capacidad_StockTotalProducto(p.getIdproducto()).get(1);
                    
                    if(aMover>stockTotal){//Se pide más de lo que hay, entonces se produce
                        aMover-=stockTotal;                        
                        especificaMovimientos(idDOP,p.getIdproducto(),aMover);
                    }
                }
            }
        }
        
        
    }

    public boolean opActiva_NoIniciada() {
        
        ObjectContext context = DataContext.createDataContext();
        OrdenProduccion op=this.buscarActiva(context);
        if(op!=null){
            if(/*!hayRequerimiento(op.getIdordenproduccion())&&*/!movimientosAceptados(op.getIdordenproduccion())){
                        
                Estado estado= new Estado();
                KardexService servK = new KardexService();

                op.setEstado(estado.getInactivo());
                List<DetalleOrdenProduccion> dop =op.getDetalleOrdenProduccionArray();
                for(int i=0;i<dop.size();i++){//Por cada detalle se eliminara sus movimientos pendientes
                    dop.get(i).setEstado(estado.getInactivo());
                    servK.inhabilitaMovimientos(context,dop.get(i).getIddetalleordenproduccion());// coloco como inactivos los movimientos                   
                    context.commitChanges();
                }
                return true;
            }
            else 
                return false;        
        }
        else{
            return true;
        }
    }
//
    public OrdenProduccion buscarActiva() {
        Estado e = new Estado();
        ObjectContext context = DataContext.createDataContext();
        Expression qualifier = ExpressionFactory.matchAllExp(OrdenProduccion.ESTADO_PROPERTY,e.getActivo());
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        List<OrdenProduccion> op = (ArrayList<OrdenProduccion>) context.performQuery(select);    
        if(op.isEmpty())
            return null;
        else
            return op.get(0);
    }
    public OrdenProduccion buscarEnProceso() {
        Estado e = new Estado();
        ObjectContext context = DataContext.createDataContext();
        Expression qualifier = ExpressionFactory.matchAllExp(OrdenProduccion.ESTADO_PROPERTY,e.getEnProceso());
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        List<OrdenProduccion> op = (ArrayList<OrdenProduccion>) context.performQuery(select);    
        if(op.isEmpty())
            return null;
        else
            return op.get(0);
    }
    public OrdenProduccion buscarActiva(ObjectContext c) {
        Expression qualifier = ExpressionFactory.matchAllExp(OrdenProduccion.ESTADO_PROPERTY,1);
        SelectQuery select = new SelectQuery(OrdenProduccion.class, qualifier);
        List<OrdenProduccion> op = (ArrayList<OrdenProduccion>) c.performQuery(select); 
        if(op.isEmpty())
            return null;
        else
            return op.get(0);
    }
    private boolean hayRequerimiento(int idop) {
         ObjectContext context = DataContext.createDataContext();
         RequerimientoService servR = new RequerimientoService();
         
         Requerimiento req = servR.buscarPorIdOrdProd(context, idop);
         if(req==null)
             return false;
         else 
             return true;
    }

   public boolean movimientosAceptados(int idop) {
        Estado  est = new Estado();
        List<DetalleOrdenProduccion> lista =  buscarDetallePorIdOP(idop);
        
        for(int i=0;i<lista.size();i++){
            if(lista.get(i).getEstado()==est.getCumplido())//se ha hecho el movimiento para producir
                return true;
        }
        return false;
    }
    public boolean movimientosDetalleAceptados(int idop,int prod) {
        
        ObjectContext c = DataContext.createDataContext();
       
        Estado  est = new Estado();
        //buscamos tipo de salida interna
        Expression q4 = ExpressionFactory.matchExp(TipoMovimiento.IDTIPOMOVIMIENTO_PK_COLUMN,3);
        SelectQuery s = new SelectQuery(TipoMovimiento.class, q4);
        TipoMovimiento t= (TipoMovimiento) DataObjectUtils.objectForQuery(c, s);
        
        
        Expression q3 = ExpressionFactory.matchExp(Kardex.DOCASIGNADO_PROPERTY,idop);
        Expression q5 = ExpressionFactory.matchExp(Kardex.TO_TIPO_MOVIMIENTO_PROPERTY,t);
        Expression q= q3.andExp(q5);
        SelectQuery s1 = new SelectQuery(Kardex.class, q);
        ArrayList<Kardex> lista = (ArrayList<Kardex>)c.performQuery(s1);
        
        if(!lista.isEmpty()){
           
        boolean movCompleto = true; 
                
        for(int i=0;i<lista.size();i++){
            //Se tiene que analizar si todos los movimientos relativos al producto que fueron aceptados
            if(lista.get(i).getToAlmacenXProducto().getToProducto().getIdproducto()==prod){
                //En caso detecte un mov de un almacen no aceptado 
                if(lista.get(i).getEstado()!=est.getAceptado()){
                    movCompleto=false;//Esto quiere decir que no todo el requerimiento para el mes esta completo
                }
            }
        }
        return movCompleto;
        }
        else 
            return false;
    }
    public List<DetalleOrdenProduccion> buscarDetallePorIdOP(int idop){
        
        ObjectContext c = DataContext.createDataContext();
        String cadena = "SELECT * FROM Detalle_Orden_Produccion WHERE ESTADO > 0 and idordenproduccion = "+idop+"";
        SQLTemplate query = new SQLTemplate(DetalleOrdenProduccion.class,cadena);        
        List<DetalleOrdenProduccion> detalles = c.performQuery(query); 
        return detalles;
    }
    public DetalleOrdenProduccion buscarDetalleOrdProdPorIdDOP(ObjectContext c,int idDOP){
                
        Expression q1 = ExpressionFactory.matchExp(DetalleOrdenProduccion.IDDETALLEORDENPRODUCCION_PK_COLUMN,idDOP);
        Expression q2= ExpressionFactory.greaterExp(DetalleOrdenProduccion.ESTADO_PROPERTY,0);
        Expression q0=q1.andExp(q2);
        SelectQuery s = new SelectQuery(DetalleOrdenProduccion.class, q0);
        return (DetalleOrdenProduccion) DataObjectUtils.objectForQuery(c, s);
    }
    
    public int buscarIdDetalleOperacion(int idOrdProd,int idProd){//brinda el cod del detalle de la orden de produccion al darle el id de la orden de prod y el prod
    
        List<DetalleOrdenProduccion> lista = buscarDetallePorIdOP(idOrdProd);
        for(int i=0;i<lista.size();i++){
            if(lista.get(i).getToProducto().getIdproducto()==idProd)
                return lista.get(i).getIddetalleordenproduccion();
        }
        return 0;//En caso en dicho detalle no se encuentre ese producto
        
    }

    private boolean verificaCumplidos(OrdenProduccion op, int dop) {
        Estado e = new Estado();
        boolean cond = true;
        List<DetalleOrdenProduccion> list = op.getDetalleOrdenProduccionArray();
        for(int i=0;i<=list.size();i++){
            if(list.get(i).getIddetalleordenproduccion()!= dop){              
                if(list.get(i).getEstado()!=e.getCumplido())
                    cond = false;
            }
        }
        return cond;
    }
}
